
(*!
	Model Factory.
	create the additional scenenodes for ( bullets, health... )

	Defines the Entities for Quake3
*)

unit Q3Factory;

interface

uses
  DIrr, DIrr.Types, DIrr.Quake3, DIrr.ConcreteMesh, DIrr.Scene, DIrr.Vector3D,
  DIrr.GUI, DIrr.Video, DIrr.Buffer, DIrr.Structs;

//! Defines to which group the entities belong
type
  TItemGroup =
  (
    igWeapon,
    igAmmo,
    igArmor,
    igHealth,
    igPowerup
  );

//! define a supgroup for the item. for e.q the Weapons
type
  TItemSubGroup =
  (
    isgSubNone,
    isgGauntlet,
    isgMachineGun,
    isgShotGun,
    isgGrenadeLauncher,
    isgRocketLauncher,
    isgLighting,
    isgRailGun,
    isgPlasmaGun,
    isgBfg,
    isgGrapplingHook,
    isgNailGun,
    isgProxLauncher,
    isChainGun
  );

//! aplly a special effect to the shader
type
  TItemSpecialEffect =
  (
    iseSpecialSfxNone,  iseSpecialSfxRotate,  iseSpecialSfxBounce,  iseSpecialSfxRotate1,
    _iseOccupyBit5,     _iseOccupyBit6,       _iseOccupyBit7,       _iseOccupyBit8,

    _iseOccupyBit9,   _iseOccupyBit10,  _iseOccupyBit11,  _iseOccupyBit12,
    _iseOccupyBit13,  _iseOccupyBit14,  _iseOccupyBit15,  _iseOccupyBit16,

    _iseOccupyBit17,  _iseOccupyBit18,  _iseOccupyBit19,  _iseOccupyBit20,
    _iseOccupyBit21,  _iseOccupyBit22,  _iseOccupyBit23,  _iseOccupyBit24,

    _iseOccupyBit25,  _iseOccupyBit26,  _iseOccupyBit27,  _iseOccupyBit28,
    _iseOccupyBit29,  _iseOccupyBit30,  _iseOccupyBit31,  _iseOccupyBit32
  );
  TItemSpecialEffectSet = set of TItemSpecialEffect;

  TItemSpecialEffectSetHelper = record helper for TItemSpecialEffectSet
  public
    function AsU32: U32; inline;
  end;


// a List for defining a model
type
  SItemElement = record
    Key: PAnsiChar;
    Model: array[0..1] of PAnsiChar;
    Sound: PAnsiChar;
    Icon: PAnsiChar;
    Pickup: PAnsiChar;
    Value: S32;
    Group: TItemGroup;
    Sub: TItemSubGroup;
    Special: TItemSpecialEffectSet; //U32;
  end;
  PItemElement = ^SItemElement;

//! Get's an entity based on it's key
function GetItemElement(const Key: PAnsiChar): PItemElement;

(*!
	Quake3 Model Factory.
	Takes the mesh buffers and creates scenenodes for their associated shaders
*)
procedure Q3ShaderFactory
(
  var LoadParam: TQ3LevelLoadParameter;
  Device: TIrrlichtDevice;
  Mesh: TQ3LevelMesh;
  MeshIndex: TQ3MeshIndex;
  Parent: TSceneNode;
  Meta: TMetaTriangleSelector;
  ShowShaderName: Boolean
);

(*!
	Creates Model based on the entity list
*)
procedure Q3ModelFactory
(
  var LoadParam: TQ3LevelLoadParameter;
  Device: TIrrlichtDevice;
  MasterMesh: TQ3LevelMesh;
  Parent: TSceneNode;
  ShowShaderName: Boolean
);

(*!
	so we need a good starting Position in the level.
	we can ask the Quake3 Loader for all entities with class_name "info_player_deathmatch"
*)
function Q3StartPosition
(
  Mesh: TQ3LevelMesh;
  Camera: TCameraSceneNode;
  StartPosIndex: S32;
  Translation: TVector3DF
): S32;

(*!
	gets a accumulated force on a given surface
*)
procedure GetGravity(const Surface: PAnsiChar; var Ret: TVector3DF);

(*
	Dynamically load the Irrlicht Library
*)
function load_createDeviceEx(const FileName: AnsiString): TCreateDeviceEx_Fun;

//! Macro for save Dropping an Element
//procedure DropElement(x);
//begin
//  if x <> 0 then
//  begin
//    x.Remove;
//    x := 0;
//  end;
//end;

(*
	get the current collision respone camera animator
*)
function CamCollisionResponse(Device: TIrrlichtDevice): TSceneNodeAnimatorCollisionResponse;


//! internal Animation
type
  TTimeFireFlag =
  (
    tffFired = 1
  );

  TTimeFire = record
    Flags: U32;
    Next: U32;
    Delta: U32;
  end;
  PTimeFire = ^TTimeFire;

procedure SetTimeFire(t: PTimeFire; Delta: U32; Flags: U32 = 0);
procedure CheckTimeFire(t: PTimeFire; ListSize: U32; NowV: U32);
      type TTest = record v1: Integer; v2: Integer; end;
//! This list is based on the original quake3.
const
  Quake3ItemElement: array[1..22] of SItemElement =
  (
    (
      Key : 'item_health';
      Model : (
                'models\powerups\health\medium_cross.md3',
                'models\powerups\health\medium_sphere.md3'
              );
      Sound : 'sound\items\n_health.wav';
      Icon : 'icons\iconh_yellow';
      Pickup : '25 Health';
      Value : 25;
      Group : igHealth;
      Sub : isgSubNone;
      Special : [iseSpecialSfxBounce, iseSpecialSfxRotate1];
    ), //1
    (
      Key : 'item_health_large';
      Model : (
                'models\powerups\health\large_cross.md3',
                'models\powerups\health\large_sphere.md3'
              );
      Sound : 'sound\items\l_health.wav';
      Icon : 'icons\iconh_red';
      Pickup : '50 Health';
      Value : 50;
      Group : igHealth;
      Sub : isgSubNone;
      Special : [iseSpecialSfxBounce, iseSpecialSfxRotate1];
    ), //2
    (
      Key : 'item_health_mega';
      Model : (
                'models\powerups\health\mega_cross.md3',
                'models\powerups\health\mega_sphere.md3'
              );
      Sound : 'sound\items\m_health.wav';
      Icon : 'icons\iconh_mega';
      Pickup : 'Mega Health';
      Value : 100;
      Group : igHealth;
      Sub : isgSubNone;
      Special : [iseSpecialSfxBounce, iseSpecialSfxRotate1];
    ), //3
    (
      Key : 'item_health_small';
      Model : (
                'models\powerups\health\small_cross.md3',
                'models\powerups\health\small_sphere.md3'
              );
      Sound : 'sound\items\s_health.wav';
      Icon : 'icons\iconh_green';
      Pickup : '5 Health';
      Value : 5;
      Group : igHealth;
      Sub : isgSubNone;
      Special : [iseSpecialSfxBounce, iseSpecialSfxRotate1];
    ), //4
    (
      Key : 'ammo_bullets';
      Model : (
                'models\powerups\ammo\machinegunam.md3',
                ''
              );
      Sound : 'sound\misc\am_pkup.wav';
      Icon : 'icons\icona_machinegun';
      Pickup : 'Bullets';
      Value : 50;
      Group : igAmmo;
      Sub : isgMachineGun;
      Special : [iseSpecialSfxBounce];
    ), //5
    (
      Key : 'ammo_bullets';
      Model : (
                'models\powerups\ammo\machinegunam.md3',
                ''
              );
      Sound : 'sound\misc\am_pkup.wav';
      Icon : 'icons\icona_machinegun';
      Pickup : 'Bullets';
      Value : 50;
      Group : igAmmo;
      Sub : isgMachineGun;
      Special : [iseSpecialSfxBounce];
    ), //6
    (
      Key : 'ammo_rockets';
      Model : (
                'models\powerups\ammo\rocketam.md3',
                ''
              );
      Sound : '';
      Icon : 'icons\icona_rocket';
      Pickup : 'Rockets';
      Value : 5;
      Group : igAmmo;
      Sub : isgMachineGun;
      Special : [iseSpecialSfxBounce];
    ), //7
    (
      Key : 'ammo_shells';
      Model : (
                'models\powerups\ammo\shotgunam.md3',
                ''
              );
      Sound : 'sound\misc\am_pkup.wav';
      Icon : 'icons\icona_shotgun';
      Pickup : 'Shells';
      Value : 10;
      Group : igAmmo;
      Sub : isgShotGun;
      Special : [iseSpecialSfxRotate];
    ), //8
    (
      Key : 'ammo_slugs';
      Model : (
                'models\powerups\ammo\railgunam.md3',
                ''
              );
      Sound : 'sound\misc\am_pkup.wav';
      Icon : 'icons\icona_railgun';
      Pickup : 'Slugs';
      Value : 10;
      Group : igAmmo;
      Sub : isgRailGun;
      Special : [iseSpecialSfxRotate];
    ), //9
    (
      Key : 'item_armor_body';
      Model : (
                'models\powerups\armor\armor_red.md3',
                ''
              );
      Sound : 'sound\misc\ar2_pkup.wav';
      Icon : 'icons\icona_red';
      Pickup : 'Heavy Armor';
      Value : 100;
      Group : igArmor;
      Sub : isgSubNone;
      Special : [iseSpecialSfxRotate];
    ), //10
    (
      Key : 'item_armor_combat';
      Model : (
                'models\powerups\armor\armor_yel.md3',
                ''
              );
      Sound : 'sound\misc\ar2_pkup.wav';
      Icon : 'icons\iconr_yellow';
      Pickup : 'Armor';
      Value : 50;
      Group : igArmor;
      Sub : isgSubNone;
      Special : [iseSpecialSfxRotate];
    ), //11
    (
      Key : 'item_armor_shard';
      Model : (
                'models\powerups\armor\shard.md3',
                ''
              );
      Sound : 'sound\misc\ar1_pkup.wav';
      Icon : 'icons\icona_shard';
      Pickup : 'Armor Armor';
      Value : 5;
      Group : igArmor;
      Sub : isgSubNone;
      Special : [iseSpecialSfxRotate];
    ), //12
    (
      Key : 'weapon_gauntlet';
      Model : (
                'models\weapons2\gauntlet\gauntlet.md3',
                ''
              );
      Sound : 'sound\misc\w_pkup.wav';
      Icon : 'icons\iconw_gauntlet';
      Pickup : 'Gauntlet';
      Value : 0;
      Group : igWeapon;
      Sub : isgGauntlet;
      Special : [iseSpecialSfxRotate];
    ), //13
    (
      Key : 'weapon_shotgun';
      Model : (
                'models\weapons2\gauntlet\shotgun.md3',
                ''
              );
      Sound : 'sound\misc\w_pkup.wav';
      Icon : 'icons\iconw_shotgun';
      Pickup : 'Shotgun';
      Value : 10;
      Group : igWeapon;
      Sub : isgShotGun;
      Special : [iseSpecialSfxRotate];
    ), //14
    (
      Key : 'weapon_machinegun';
      Model : (
                'models\weapons2\machinegun\machinegun.md3',
                ''
              );
      Sound : 'sound\misc\w_pkup.wav';
      Icon : 'icons\iconw_machinegun';
      Pickup : 'Machinegun';
      Value : 40;
      Group : igWeapon;
      Sub : isgMachineGun;
      Special : [iseSpecialSfxRotate];
    ), //15
    (
      Key : 'weapon_grenadelauncher';
      Model : (
                'models\weapons2\grenadel\grenadel.md3',
                ''
              );
      Sound : 'sound\misc\w_pkup.wav';
      Icon : 'icons\iconw_grenade';
      Pickup : 'Grenade Launcher';
      Value : 10;
      Group : igWeapon;
      Sub : isgGrenadeLauncher;
      Special : [iseSpecialSfxRotate];
    ), //16
    (
      Key : 'weapon_rocketlauncher';
      Model : (
                'models\weapons2\rocketl\rocketl.md3',
                ''
              );
      Sound : 'sound\misc\w_pkup.wav';
      Icon : 'icons\iconw_rocket';
      Pickup : 'Rocket Launcher';
      Value : 10;
      Group : igWeapon;
      Sub : isgRocketLauncher;
      Special : [iseSpecialSfxRotate];
    ), //17
    (
      Key : 'weapon_lightning';
      Model : (
                'models\weapons2\lightning\lightning.md3',
                ''
              );
      Sound : 'sound\misc\w_pkup.wav';
      Icon : 'icons\iconw_lightning';
      Pickup : 'Lightning Gun';
      Value : 100;
      Group : igWeapon;
      Sub : isgLighting;
      Special : [iseSpecialSfxRotate];
    ), //18
    (
      Key : 'weapon_railgun';
      Model : (
                'models\weapons2\railgun\railgun.md3',
                ''
              );
      Sound : 'sound\misc\w_pkup.wav';
      Icon : 'icons\iconw_railgun';
      Pickup : 'Railgun';
      Value : 10;
      Group : igWeapon;
      Sub : isgRailgun;
      Special : [iseSpecialSfxRotate];
    ), //19
    (
      Key : 'weapon_plasmagun';
      Model : (
                'models\weapons2\plasma\plasma.md3',
                ''
              );
      Sound : 'sound\misc\w_pkup.wav';
      Icon : 'icons\iconw_plasma';
      Pickup : 'Plasma Gun';
      Value : 50;
      Group : igWeapon;
      Sub : isgPlasmaGun;
      Special : [iseSpecialSfxRotate];
    ), //20
    (
      Key : 'weapon_bfg';
      Model : (
                'models\weapons2\bfg\bfg.md3',
                ''
              );
      Sound : 'sound\misc\w_pkup.wav';
      Icon : 'icons\iconw_bfg';
      Pickup : 'BFG10K';
      Value : 20;
      Group : igWeapon;
      Sub : isgBfg;
      Special : [iseSpecialSfxRotate];
    ), //21
    (
      Key : 'weapon_grapplinghook';
      Model : (
                'models\weapons2\grapple\grapple.md3',
                ''
              );
      Sound : 'sound\misc\w_pkup.wav';
      Icon : 'icons\iconw_grapple';
      Pickup : 'Grappling Hook';
      Value : 0;
      Group : igWeapon;
      Sub : isgGrapplingHook;
      Special : [iseSpecialSfxRotate];
    )  //22
  );

implementation

uses
  Windows, AnsiStrings;

function load_createDeviceEx(const FileName: AnsiString): TCreateDeviceEx_Fun;
begin
  Result := GetProcAddress(LoadLibraryA(PAnsiChar(FileName)), 'createDeviceEx');
end;

function GetItemElement(const Key: PAnsiChar): PItemElement;
begin
  {$POINTERMATH ON}
  Result := @Quake3ItemElement[Low(Quake3ItemElement)];

  while Result.Key <> nil do
  begin
    if AnsiStrings.AnsiStrComp(Result.Key, Key) = 0 then
      Exit;

    Result := Result + 1;
  end;

  Result := nil;
end;

procedure Q3ShaderFactory
(
  var LoadParam: TQ3LevelLoadParameter;
  Device: TIrrlichtDevice;
  Mesh: TQ3LevelMesh;
  MeshIndex: TQ3MeshIndex;
  Parent: TSceneNode;
  Meta: TMetaTriangleSelector;
  ShowShaderName: Boolean
);
const BUF_SIZE = 128;
var
  MeshNode: TMeshSceneNode;
  ASmgr: TSceneManager;
  ASelector: TTriangleSelector;
  AdditionalMesh: TMesh;
  Buf: array[0..BUF_SIZE - 1] of AnsiChar;
  AFont: TGUIFont;
  ADriver: TVideoDriver;
  ASceneNodeID: S32;
  i, ACount: U32;
  AMeshBuffer: TMeshBuffer;
  AMaterial: SMaterial;
  AShaderIndex: S32;
  AShaderPtr: PShader;
begin
  if (Mesh = nil) or (Device = nil) then
    Exit;

  MeshNode := nil;
  ASmgr := Device.GetSceneManager;
  ASelector := nil;

	// the additional mesh can be quite huge and is unoptimized
	// Save to cast to SMesh
  AdditionalMesh := Mesh.GetMesh(Ord(MeshIndex));
  if Assigned(AdditionalMesh) or (AdditionalMesh.GetMeshBufferCount = 0) then
    Exit;

  if LoadParam.verbose > 0 then
  begin
    LoadParam.startTime := Device.GetTimer.GetRealTime;
    if LoadParam.verbose > 1 then
    begin
      AnsiStrings.StrLCopy(@Buf[0], 'q3shaderfactory start', BUF_SIZE);
      Device.GetLogger.Log(Buf, ELL_INFORMATION);
    end;
  end;

  AFont := nil;
  if ShowShaderName then
    AFont := Device.GetGUIEnvironment.GetFont('fontlucida.png');

  ADriver := Device.GetVideoDriver;

  // create helper textures
  if True then
  begin
    //To do
  end;

  ASceneNodeID := 0;
  ACount := AdditionalMesh.GetMeshBufferCount;
  for i := 1 to ACount do
  begin
    AMeshBuffer := AdditionalMesh.GetMeshBuffer(i - 1);
    AMaterial := PMaterial(AMeshBuffer.GetMaterial)^;

    //! The ShaderIndex is stored in the second material parameter
    AShaderIndex := Round(AMaterial.MaterialTypeParam2);

    // the meshbuffer can be rendered without additional support, or it has no shader
    AShaderPtr := Mesh.GetShader(AShaderIndex);

    // no shader, or mapped to existing material
    if AShaderPtr = nil then
    begin


    end
    else if True then
    begin

    end;


  end;


end;

procedure Q3ModelFactory
(
  var LoadParam: TQ3LevelLoadParameter;
  Device: TIrrlichtDevice;
  MasterMesh: TQ3LevelMesh;
  Parent: TSceneNode;
  ShowShaderName: Boolean
);
begin

end;

function Q3StartPosition
(
  Mesh: TQ3LevelMesh;
  Camera: TCameraSceneNode;
  StartPosIndex: S32;
  Translation: TVector3DF
): S32;
begin

end;

procedure GetGravity(const Surface: PAnsiChar; var Ret: TVector3DF);
begin

end;

function CamCollisionResponse(Device: TIrrlichtDevice): TSceneNodeAnimatorCollisionResponse;
begin

end;

procedure SetTimeFire(t: PTimeFire; Delta: U32; Flags: U32 = 0);
begin

end;

procedure CheckTimeFire(t: PTimeFire; ListSize: U32; NowV: U32);
begin

end;

{ TItemSpecialEffectSet }

function TItemSpecialEffectSetHelper.AsU32: U32;
begin
  Result := PU32(@Self)^;
end;

end.
